package com.ruoyi.project.payUtils.wx.sdkV3;

import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.framework.config.RuoYiConfig;
import com.ruoyi.project.content.FlowTypeEnum;
import com.ruoyi.project.utils.JsonUtils;
import com.wechat.pay.java.core.RSAConfig;
import com.wechat.pay.java.service.payments.app.AppServiceExtension;
import com.wechat.pay.java.service.payments.app.model.Amount;
import com.wechat.pay.java.service.payments.app.model.PrepayRequest;
import com.wechat.pay.java.service.payments.app.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.app.model.QueryOrderByOutTradeNoRequest;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.transferbatch.TransferBatchService;
import com.wechat.pay.java.service.transferbatch.model.InitiateBatchTransferRequest;
import com.wechat.pay.java.service.transferbatch.model.TransferDetailInput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * V3版本微信支付
 *
 * @author 挺好的 2023年02月21日 上午 10:57
 */
@Component ("wxPayUtilsV3")
@Slf4j
public class WxPayUtilsV3 {

    /**
     * 支付配置
     */
    @Resource (name = "wxPayConfigV3")
    private WxPayConfigV3 wxPayConfigV3;



    /**
     * 获取预交易会话结果
     *
     * @param outTradeNo
     *         订单编号
     * @param flowTypeEnum
     *         流水类型
     * @param money
     *         支付金额
     * @param createTime
     *         创建时间
     * @param tradeType
     *         交易类型
     * @param openId
     *         用户open id
     * @param userId
     *         用户id
     * @param userName
     *         用户姓名
     *
     * @return
     */
    public Map <String, String> getPrePayResult (String outTradeNo, FlowTypeEnum flowTypeEnum, BigDecimal money,
            Date createTime, String tradeType, String openId, Long userId, String userName) throws Exception {
        // 组装订单编号
        outTradeNo = this.getOutTradeNo(outTradeNo, flowTypeEnum);
        //记录交易号
        RSAConfig config = new RSAConfig.Builder().merchantId(this.wxPayConfigV3.getMerchantId())
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(this.getResource(this.wxPayConfigV3.getPrivateKeyPath()))
                .merchantSerialNumber(this.wxPayConfigV3.getMerchantSerialNumber())
                .wechatPayCertificatesFromPath(this.getResource(this.wxPayConfigV3.getWechatPayCertPath())).build();

        String description = "找零工平台支付";

        // 支付金额，单位分
        Integer totalFee = money.setScale(0, RoundingMode.HALF_UP).intValue();

        Map <String, String> result = new HashMap <>();

        try {

            if ("JSAPI".equals(tradeType)) {
                JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
                com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest request = new com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest();

                com.wechat.pay.java.service.payments.jsapi.model.Amount amount = new com.wechat.pay.java.service.payments.jsapi.model.Amount();

                // 支付金额
                amount.setTotal(totalFee);
                request.setAmount(amount);

                request.setAppid(this.wxPayConfigV3.getMiniAppId());
                request.setMchid(this.wxPayConfigV3.getMerchantId());
                request.setDescription(description);
                request.setNotifyUrl(this.wxPayConfigV3.getNotifyUrl());
                request.setOutTradeNo(outTradeNo);
                Payer payer = new Payer();
                payer.setOpenid(openId);
                request.setPayer(payer);

                com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(
                        request);

                log.info("微信支付V3[JSAPI]，传输参数:{}", JsonUtils.objectToJson(request));
                log.info("微信支付V3[JSAPI]，预交易会话返回数据：{}", JsonUtils.objectToJson(response));

                this.setResponse(result, response);
            } else {
                AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();
                PrepayRequest request = new PrepayRequest();
                request.setAppid(this.wxPayConfigV3.getAppId());
                request.setMchid(this.wxPayConfigV3.getMerchantId());
                request.setDescription(description);
                request.setNotifyUrl(this.wxPayConfigV3.getNotifyUrl());
                request.setOutTradeNo(outTradeNo);

                Amount amount = new Amount();
                amount.setTotal(totalFee);
                request.setAmount(amount);

                PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request);

                log.info("微信支付V3[APP]，传输参数:{}", JsonUtils.objectToJson(request));
                log.info("微信支付V3[APP]，预交易会话返回数据：{}", JsonUtils.objectToJson(response));

                this.setResponse(result, response);
            }

        } catch (Exception e) {
            log.error("微信支付V3[{}]预交易失败，{}", "JSAPI".equals(tradeType) ? "JSAPI" : "APP", e);
            throw new RuntimeException(e.getMessage());
        }

        return result;
    }

    /**
     * 退款
     *
     * @param outTradeNo
     * @param outRefundNo
     * @param amount
     */
    public Map <String, String> refund (String outTradeNo, String outRefundNo, BigDecimal amount, Integer count) {

        RSAConfig config = new RSAConfig.Builder().merchantId(this.wxPayConfigV3.getMerchantId())
                // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                .privateKeyFromPath(this.getResource(this.wxPayConfigV3.getPrivateKeyPath()))
                .merchantSerialNumber(this.wxPayConfigV3.getMerchantSerialNumber())
                .wechatPayCertificatesFromPath(this.getResource(this.wxPayConfigV3.getWechatPayCertPath())).build();

        RefundService service = new RefundService.Builder().config(config).build();
        return null;
    }


    /**
     * 转账
     *
     * @param openId
     * @param userName
     * @param withDrawId
     * @param amount
     */
    public void transfer (String openId, String userName, String withDrawId, BigDecimal amount) {

        try {
            Long totalAmount = amount.setScale(0, RoundingMode.HALF_UP).longValue();

            RSAConfig config = new RSAConfig.Builder().merchantId(this.wxPayConfigV3.getMerchantId())
                    // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                    .privateKeyFromPath(this.getResource(this.wxPayConfigV3.getPrivateKeyPath()))
                    .merchantSerialNumber(this.wxPayConfigV3.getMerchantSerialNumber())
                    .wechatPayCertificatesFromPath(this.getResource(this.wxPayConfigV3.getWechatPayCertPath())).build();

            TransferBatchService service = new TransferBatchService.Builder().config(config).build();

            InitiateBatchTransferRequest initiateBatchTransferRequest = new InitiateBatchTransferRequest();
            initiateBatchTransferRequest.setAppid(this.wxPayConfigV3.getAppId());
            initiateBatchTransferRequest.setOutBatchNo(withDrawId);
            initiateBatchTransferRequest.setBatchName("找零工提现");
            initiateBatchTransferRequest.setBatchRemark("找零工提现");
            initiateBatchTransferRequest.setTotalAmount(totalAmount);
            initiateBatchTransferRequest.setTotalNum(1);

            List <TransferDetailInput> transferDetailListList = new ArrayList <>();

            {
                TransferDetailInput transferDetailInput = new TransferDetailInput();

                transferDetailInput.setOutDetailNo("detail" + withDrawId);
                transferDetailInput.setTransferAmount(totalAmount);
                transferDetailInput.setTransferRemark("找零工提现");

                transferDetailInput.setOpenid(openId);

                // 规定 < 0.3元的时候，不允许填写收款用户姓名
                if (totalAmount <= 30) {
                    transferDetailInput.setUserName(null);
                } else {
                    transferDetailInput.setUserName(userName);
                }

                transferDetailListList.add(transferDetailInput);
            }

            initiateBatchTransferRequest.setTransferDetailList(transferDetailListList);

            service.initiateBatchTransfer(initiateBatchTransferRequest);
        } catch (Exception e) {
            log.error("微信转账到零钱失败", e);
        }

    }


    /**
     * 订单查询
     *
     * @param outTradeNo
     *         商户交易号（系统交易的编号）
     */
    public String orderQuery (String outTradeNo) {

        try {
            RSAConfig config = new RSAConfig.Builder().merchantId(this.wxPayConfigV3.getMerchantId())
                    // 使用 com.wechat.pay.java.core.util 中的函数从本地文件中加载商户私钥，商户私钥会用来生成请求的签名
                    .privateKeyFromPath(this.getResource(this.wxPayConfigV3.getPrivateKeyPath()))
                    .merchantSerialNumber(this.wxPayConfigV3.getMerchantSerialNumber())
                    .wechatPayCertificatesFromPath(this.getResource(this.wxPayConfigV3.getWechatPayCertPath())).build();

            AppServiceExtension service = new AppServiceExtension.Builder().config(config).build();

            QueryOrderByOutTradeNoRequest queryRequest = new QueryOrderByOutTradeNoRequest();
            queryRequest.setMchid(this.wxPayConfigV3.getMerchantId());
            queryRequest.setOutTradeNo(outTradeNo);

            Transaction result = service.queryOrderByOutTradeNo(queryRequest);

            if (!"SUCCESS".equals(result.getTradeState())) {
                return result.getTradeStateDesc();
            }
        } catch (Exception e) {
            return e.getMessage();
        }

        return "查询成功";
    }

    /**
     * 获取订单编号
     *
     * @param outTradeNo
     *         订单编号
     * @param flowTypeEnum
     *         流水类型
     *
     * @return
     */
    private String getOutTradeNo (String outTradeNo, FlowTypeEnum flowTypeEnum) {
        return flowTypeEnum.getValue() + "-" + outTradeNo + "-" + DateUtils.getNowDate().getTime();
    }

    /**
     * APP支付结果返回
     *
     * @param result
     * @param response
     */
    private void setResponse (Map <String, String> result, PrepayWithRequestPaymentResponse response) {
        result.put("appid", response.getAppid());
        result.put("partnerid", response.getPartnerId());
        result.put("package", response.getPackageVal());
        result.put("prepayid", response.getPrepayId());
        result.put("noncestr", response.getNonceStr());
        result.put("timestamp", response.getTimestamp());
        result.put("sign", response.getSign());
    }

    /**
     * APP支付结果返回
     *
     * @param result
     * @param response
     */
    private void setResponse (Map <String, String> result,
            com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse response) {
        result.put("appId", response.getAppId());
        result.put("package", response.getPackageVal());
        result.put("nonceStr", response.getNonceStr());
        result.put("timeStamp", response.getTimeStamp());
        result.put("signType", response.getSignType());
        result.put("sign", response.getPaySign());
    }

    /**
     * 获取文件所在文字
     *
     * @param resource
     *
     * @return
     */
    private String getResource (String resource) {

        return RuoYiConfig.getProfile() + resource;

    }

}
